#include "TwoWayList.h"
#include "Record.h"
#include "Schema.h"
#include "File.h"
#include "Comparison.h"
#include "ComparisonEngine.h"
#include "DBFile.h"
#include "Defs.h"
#include <iostream>
#include <fstream>
#include <stdlib.h>

// stub file .. replace it with your own DBFile.cc

DBFile::DBFile () {
}

// not finished yet, need to create according to f_type
int DBFile :: Create (char *f_path, fType f_type, void *startup) {
    ofstream metadataFile;
    char m_name[100];
    sprintf(m_name,"%s.md",f_path);
    metadataFile.open(m_name);

    switch(f_type){
        case heap:
            metadataFile << "heap";
            myInternalVar = new HeapDBFile;
            break;
        case sorted:
            metadataFile << "sorted"<<endl;
            metadataFile<<((SortInfo *)startup) ->runLength<<endl;
            ((SortInfo*)startup)->myOrder->WriteToMetaFile(metadataFile);
            myInternalVar=new SortedDBFile((SortInfo*)startup);
            break;
 /*         dbFile.Open(0, f_path);
            if(dbFile.GetFilDes() < 0)
                return 0;
            else
                return 1;
*/
        default:
            cout<<"not a valid type!"<<endl;
            return 0;
            break;

    }
    myInternalVar->Create(f_path);
    metadataFile.close();
}

void DBFile :: Load (Schema &f_schema, char *loadpath) {
    // success in open file?
    // deal with multiple call for this function?
    // for now, suppose this function is used to initialize and use only once

/*    FILE* inputFile = fopen(loadpath,"r");
    Record temp;
    // whichPage is not neccesary
    off_t whichPage = 0;
    while(temp.SuckNextRecord(&f_schema, inputFile) == 1){
        if(dbPage.Append(&temp) == 0) {
            dbFile.AddPage(&dbPage, whichPage++);
            dbPage.EmptyItOut();
            dbPage.Append(&temp);
        }
    }
    dbFile.AddPage(&dbPage, whichPage);
    dbPage.EmptyItOut();
    MoveFirst();
*/
    myInternalVar->Load(f_schema, loadpath);
}

void DBFile :: AdditionAddPage(){
    myInternalVar->AdditionAddPage();
}
int DBFile :: Open (char *f_path) {
    ifstream metadataFile;
    string f_type;
    char m_name[100];
    sprintf(m_name,"%s.md",f_path);
    metadataFile.open(m_name);
    if(!metadataFile.is_open()){
        cerr<<"metadata does not exist!"<<endl;
        return 0;
    }
    getline(metadataFile,f_type);
    if (f_type.compare("heap")==0){
        type = heap;
        myInternalVar=new HeapDBFile;
    }


    else if(f_type.compare("sorted")==0) {
        type = sorted;
        string runLength, whichAtt,numAtt;
        SortInfo *sortInfo=new SortInfo;

        getline(metadataFile,runLength);
        getline(metadataFile,numAtt);


        int runlen=atoi(runLength.c_str());
        int numAtts=atoi(numAtt.c_str());
        if(runlen==0 || numAtts ==0){
            cout<<"Bad!  Corrupt Metadata File.Exiting..."<<endl;
            exit(1);
        }

        sortInfo->runLength=runlen;
        int whichAtts[numAtts];
        Type whichTypes[numAtts];

        for(int i=0;i<numAtts;i++){

            getline(metadataFile,whichAtt);
            char* att=strtok((char *)whichAtt.c_str(),",");
            char* type=strtok(NULL,",");

            whichAtts[i]= atoi(att);

            if(strcmp(type,"Int")==0)
                whichTypes[i]=Int;
            else if(strcmp(type,"Double")==0)
                whichTypes[i]=Double;
            else if(strcmp(type,"String")==0)
                whichTypes[i]=String;
            else{
                    cout<<"Bad!  Corrupt Metadata File.Exiting..."<<endl;
                    exit(1);
            }

        }

        sortInfo->myOrder = new OrderMaker(numAtts, whichAtts, whichTypes);
        //cerr<<"Metadata Info\n";
        //sortInfo->myOrder->Print();
        myInternalVar=new SortedDBFile(sortInfo);
    }

    else{
        cout<<"not a valid type!"<<endl;
        return 0;
    }
    myInternalVar->Open(f_path);


 /*   dbFile.Open(1, f_path);
    if(dbFile.GetFilDes() < 0)
        return 0;
    else
        return 1;
*/
}

void DBFile :: MoveFirst () {
/*    if(dbFile.GetFilDes() < 0)
        return;
    curPageInFile = 0;
*/
    myInternalVar->MoveFirst();
}

int DBFile :: Close () {
/*    if(dbFile.Close() == 0)
        return 0;
    else
        return 1;
*/
    myInternalVar->Close();
}

void DBFile :: Add (Record &addme) {
    // for now, no check on whether still can insert into bin file or not
/*    if(isDirty) {
        if(dbPage.Append(&addme) == 0) {
            if(dbFile.GetLength() == 0)
                dbFile.AddPage(&dbPage, dbFile.GetLength());
            else
                dbFile.AddPage(&dbPage, dbFile.GetLength() - 1);
            dbPage.EmptyItOut();
            dbPage.Append(&addme);
        }
    }
    else {
        dbPage.EmptyItOut();
        dbPage.Append(&addme);
        isDirty = true;
    }
*/
    myInternalVar->Add(addme);
}

int DBFile :: GetNext (Record &fetchme) {
/*    if(!isDirty) {
        if(dbPage.GetFirst(&curRecord) == 1) {
            fetchme.Consume(&curRecord);
            return 1;
        }
        else {
            if(curPageInFile >= dbFile.GetLength() - 1) {
                return 0;
            }
            else {
                dbPage.EmptyItOut();
                dbFile.GetPage(&dbPage, curPageInFile++);
                dbPage.GetFirst(&curRecord);
                fetchme.Consume(&curRecord);
                return 1;
            }
        }
    }
    else {
        if(dbFile.GetLength() == 0)
            dbFile.AddPage(&dbPage, dbFile.GetLength());
        else
            dbFile.AddPage(&dbPage, dbFile.GetLength() - 1);
        dbPage.EmptyItOut();
        dbFile.GetPage(&dbPage, curPageInFile++);
        dbPage.GetFirst(&curRecord);
        fetchme.Consume(&curRecord);
        isDirty = false;
        return 1;
    }
*/
    myInternalVar->GetNext(fetchme);
}

int DBFile :: GetNext (Record &fetchme, CNF &cnf, Record &literal) {
/*    ComparisonEngine comp;
    Record temp;
    while(GetNext(temp) == 1) {
        if(comp.Compare(&temp, &literal, &cnf)) {
            fetchme.Consume(&temp);
            return 1;
        }
    }
    return 0;
*/
    myInternalVar->GetNext(fetchme,cnf,literal);
}
